﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using GdNet.Core.Assistants;
using GdNet.Integrations.NHibernate.Mapping;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Mapping.ByCode;

namespace GdNet.Integrations.NHibernate.SessionManagement
{
    public class SessionFactoryBuilder
    {
        private string _hibernateConfiguration;
        private readonly IList<IInterceptor> _interceptors = new List<IInterceptor>();
        private readonly IList<string> _mappingFiles = new List<string>();

        private SessionFactoryBuilder()
        {
        }

        public static SessionFactoryBuilder NewInstance()
        {
            return new SessionFactoryBuilder();
        }

        public SessionFactoryBuilder UseConfigurationFile(string configurationFile)
        {
            _hibernateConfiguration = configurationFile;
            return this;
        }

        public SessionFactoryBuilder AddInterceptor(IInterceptor interceptor)
        {
            _interceptors.Add(interceptor);
            return this;
        }

        public SessionFactoryBuilder WithMappingFile(string mappingAssemblyFileName)
        {
            if (!File.Exists(mappingAssemblyFileName))
            {
                throw new FileNotFoundException(string.Format("The file {0} was not found", mappingAssemblyFileName));
            }
            _mappingFiles.Add(mappingAssemblyFileName);
            return this;
        }

        /// <param name="mappingAssemblyFileNames">A list of mapping assemblies which separated by ';'</param>
        public SessionFactoryBuilder WithMappingFiles(string mappingAssemblyFileNames)
        {
            mappingAssemblyFileNames.Split(';').ToList().ForEach(x => WithMappingFile(x));
            return this;
        }

        public ISessionFactory BuildSessionFactory()
        {
            var nhConfiguration = new Configuration();
            if (File.Exists(_hibernateConfiguration))
            {
                nhConfiguration.Configure(_hibernateConfiguration);
            }

            _interceptors.ToList().ForEach(interceptor => nhConfiguration.SetInterceptor(interceptor));

            var mapper = new ModelMapper();
            _mappingFiles.ToList().ForEach(mappingAssemblyFile =>
            {
                var asm = Assembly.LoadFile(mappingAssemblyFile);
                var listeMappingTypes = ReflectionAssistant.GetTypesImplementedInterfaceOnAssembly(typeof(IEntityMapping), asm);
                mapper.AddMappings(listeMappingTypes);
            });
            nhConfiguration.AddDeserializedMapping(mapper.CompileMappingForAllExplicitlyAddedEntities(), string.Empty);

            return nhConfiguration.BuildSessionFactory();
        }
    }
}
